package com.iflytek.esb.servlet.controller;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.iflytek.esb.domain.Message;
import com.iflytek.esb.service.RequestService;
import com.iflytek.esb.service.RequestService.Result;
import com.iflytek.esb.util.Consts;
import com.iflytek.esb.util.UriUtil;

@Controller
public class ServletController {

	private static final Logger LOG = Logger.getLogger(ServletController.class);
	
	@Autowired
	private RequestService requestServiceWsdl;
	
	@Autowired
	private RequestService requestServiceSoap;
	
	@RequestMapping("/services/*")
	public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
		
		LOG.info("incoming message, uri: " + request.getRequestURI() + ", params: " + request.getParameterMap());
		
		//构建消息
		Message input = composeMessage(request);
		Message output = new Message();

		//调用处理器对消息进行处理
		RequestService[] requestServices = new RequestService[] {
				requestServiceWsdl, 
				requestServiceSoap
		}; 
		
		Result result = Result.NO_OPERATE;
		for (RequestService service : requestServices) {
			result = service.process(input, output);
			
			if (result != null && result != Result.NO_OPERATE) {
				break;
			}
		}
		
		//返回处理后的消息
		if (result == Result.OK) {
			responseMessage(response, output);
		}
		else if (result == Result.ERROR) {
			response.sendError(503);
		}
		else if (result == Result.TIMEOUT) {
			response.sendError(408);
		}
		else if (result == Result.NO_OPERATE) {
			response.sendError(404);
		}
	}

	private Message composeMessage(HttpServletRequest request) throws IOException {
	
		//构建消息头
		Map<String, String> headers = getHeadersFromRequest(request);
		LOG.debug("get request from web, headers: " + headers);
	
		String body = StringUtils.EMPTY;
		
		@SuppressWarnings("rawtypes")
		Map params = request.getParameterMap();
		if (!params.isEmpty()) {
			//有请求参数，从请求参数中获取消息内容
			body = getBodyFromParameterMap(params);
		}
		else {
			//没有请求参数，直接读请求内容
			String charset = getCharsetFromHeaders(headers);
			body = getBodyFromRequestStream(request, charset);
		}
		
		//设置
		Message message = new Message();
		message.setHeaders(headers);
		message.setContent(body);
		return message;
	}

	private Map<String, String> getHeadersFromRequest(HttpServletRequest request) {
		Map<String, String> headers = new HashMap<String, String>();
		@SuppressWarnings("unchecked")
		Enumeration<String> names = request.getHeaderNames();
		while (names.hasMoreElements()) {
			String name = names.nextElement();
			String value = request.getHeader(name);
			headers.put(name, value);
		}
		
		//加入一个特殊的消息头，表示接收消息的地址，去掉地址前缀
		String requestUri = request.getRequestURI();
		String prefix = request.getContextPath() + "/services";
		requestUri = StringUtils.substring(requestUri, prefix.length());
		
		String serviceName = UriUtil.getServiceName(requestUri);
		headers.put(Consts.HEADER_SERVICE_NAME, serviceName);
		headers.put(Consts.HEADER_REQUEST_URI, requestUri);
		
		return headers;
	}

	private String getBodyFromParameterMap(@SuppressWarnings("rawtypes") Map params) {
	
		Document document = DocumentHelper.createDocument();
		Element nodeElement = document.addElement("parameters");
		for (Object entry : params.keySet()) {
			
			Element paramElement = nodeElement.addElement("parameter");
			paramElement.addAttribute("name", String.valueOf(entry));
			Object o = params.get(entry);
			if (o == null) {
				paramElement.setText("");
			}
			else {
				try {
					String[] val = (String[])o;
					if (val != null && val.length > 0) {
						paramElement.setText(val[0]);
					}
				}
				catch (Exception e) {
					paramElement.setText(String.valueOf(o));
				}
			}
			
		}
		
	    String s = StringUtils.EMPTY;
	    try {  
	        // 使用输出流来进行转化  
	        ByteArrayOutputStream out = new ByteArrayOutputStream();  
	        // 使用UTF-8编码  
	        OutputFormat format = new OutputFormat(" ", true, "UTF-8");  
	        XMLWriter writer = new XMLWriter(out, format);  
	        writer.write(document);  
	        s = out.toString("UTF-8");  
	    } 
	    catch (Exception ex) {  
	        //  
	    }  
	    return s;  
	
	}

	private String getCharsetFromHeaders(Map<String, String> headers) {
		String charset = "utf-8";
		
		if (!headers.containsKey("Content-Type")) {
			return charset;
		}
		
		String contentType = headers.get("Content-Type");
		
		String[] parts = StringUtils.split(contentType, ";");
		for (String part : parts) {
			String[] keyValue = StringUtils.split(part, "=");
			if (keyValue.length > 1) {
				String key = StringUtils.trim(keyValue[0]);
				String value = StringUtils.trim(keyValue[1]);
				if (StringUtils.equals(key, "charset")) {
					charset = value;
					break;
				}
			}
		}
	
		//这里最好是使用标准charset检查，防止charset错误
		//不过不检查也没关系，请求字符串写错了是客户端的职责，服务端不检查也是正常的，只需要有异常抛出
		return charset;
	}

	private String getBodyFromRequestStream(HttpServletRequest request, String charset) throws IOException {
		InputStream is = null;
		try {
			is = request.getInputStream();
			List<String> lines = IOUtils.readLines(is, charset);
			StringBuilder s = new StringBuilder("");
			for (String line : lines) {
				s.append(line).append("\n");
			}
			
			return s.toString();
		}
		finally {
			try {is.close(); } catch (Exception e) {}
		}
	}

	private void responseMessage(HttpServletResponse response, Message message) throws IOException {
		 
		for (Entry<String, String> entry : message.getHeaders().entrySet()) {
			response.setHeader(entry.getKey(), entry.getValue());
		}
	
		LOG.debug("response back to client, headers: " + message.getHeaders());
		String charset = getCharsetFromHeaders(message.getHeaders());
		IOUtils.write(message.getContent(), response.getOutputStream(), charset);
	}
}
